<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 

class Core_Layout {
	
	protected $_blocks = array();

	public function __construct() { }
	
	public function setBlock($name, $block) {
		$this->_blocks[$name] = $block;
		return $this;
	}
	
	public function getBlock($name) {
		return isset($this->_blocks[$name]) ? $this->_blocks[$name] : null;
	}
	
	public function removeBlock($name) {
		unset($this->_blocks[$name]);
		return $this;
	}
	
	public function render() {
		foreach($this->_blocks as $block)
			if (!$block->getParent()) $block->render(); // render top level block
	}
	
	protected $_layouts = array();	// xml objects containt layout denition
	protected $_handles = array();	// handles will be loaded from xml
	protected $_loadedHandles = array();

	public function addLayout($s) {
		$xml = simplexml_load_string($s);
		array_push($this->_layouts, $xml);
	}
	
	public function addHandle($h) {
		array_push($this->_handles, $h);
	}
	
	public function loadLayout() {
		// start the layout loading process
		foreach ($this->_handles as $h)
			$this->_applyHandle($h);
	}
	
	public function _applyHandle($h) {
		if (in_array($h, $this->_loadedHandles)) return;
		foreach ($this->_layouts as $l) {	// search for layout has the current handle
			$node = $l->$h;
			if (!empty($node)) {
				$parent = (string)$node['inherit'];
				if (!empty($parent)) $this->_applyHandle($parent);	// load parent handle
				$this->_parseNode($node);	// load block from curent handle
				array_push($this->_loadedHandles, $h);
			}
		}
	}
	
	protected function _parseNode($parent) {
		foreach ($parent->children() as $node) {
			switch ($node->getName()) {
				case 'block':
					$this->_generateBlock($node, $parent);
					$this->_parseNode($node);
					break;

				case 'reference':
					$this->_parseNode($node);
					break;

				case 'action':
					$this->_generateAction($node, $parent);
					break;
					
				case 'remove':
					$this->_generateRemove($node);
					break;
			}
		}
	}
	
	protected function _generateBlock($node, $parent) {
		$blockName = (string)$node['name'];
		$className = (string)$node['type'];
		$block = $this->createBlock($blockName, $className);
		$block->setTemplate((string)$node['template']);
		
		$parentBlock = $this->getBlock((string)$parent['name']);
		if ($parentBlock) $parentBlock->append($block);
	}
	
	public function createBlock($name, $class='', $data = array()) {
		if ($class=='') $class = 'Core_Block';
		$block = App::object($class);
		$this->setBlock($name, $block);
		$block
			->setName($name)
			->addData($data)
			->setLayout($this)
		;
		return $block;
	}
	
	protected function _generateAction($node, $parent) {
		$method = (string)$node['method'];
		$args = array();
		foreach($node->children() as $n)
			array_push($args, (string)$n);
		$blockName = (string)$parent['name'];
		$block = $this->getBlock($blockName);
		if ($block)	call_user_func_array(array($block, $method), $args);
	}
	
	protected function _generateRemove($node) {
		$blockName = (string)$node['name'];
		$block = $this->getBlock($blockName);
		$this->removeBlock($blockName);
	}
	
	public function applyUpdate($s) {
		$xmlTag = '<'.'?xml version="1.0"?'.'>';
		$xml = simplexml_load_string($xmlTag.'<update>'.$s.'</update>');
		$this->_parseNode($xml);
	}
	
	protected function _prepareHandles($handle) {
		$node = $this->_xml->$handle;
		if (!empty($node)) {
			$h = (string)$node['inherit'];
			if (!empty($h)) $this->_prepareHandles($h);
			array_push($this->_handles, $handle);
		}
	}
	
}